package net.goutalk.glcs.module.system.service.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.request.OapiUserGetbyunionidRequest;
import com.dingtalk.api.request.OapiV2UserGetRequest;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.dingtalk.api.response.OapiUserGetbyunionidResponse;
import com.dingtalk.api.response.OapiV2UserGetResponse;
import net.goutalk.glcs.common.constant.GlobalConstant;
import net.goutalk.glcs.common.enums.EnabledMark;
import net.goutalk.glcs.common.utils.RedisUtil;
import net.goutalk.glcs.config.DingtalkConfig;
import net.goutalk.glcs.config.LicenseConfig;
import net.goutalk.glcs.config.WechatEnterpriseConfig;
import net.goutalk.glcs.module.organization.entity.*;
import net.goutalk.glcs.module.organization.service.*;
import net.goutalk.glcs.module.organization.entity.*;
import net.goutalk.glcs.module.system.model.DingTalkModel;
import net.goutalk.glcs.module.system.model.DingTalkUserInfoModel;
import net.goutalk.glcs.module.system.model.WechatEnterpriseModel;
import net.goutalk.glcs.module.system.model.WechatEnterpriseUserInfoModel;
import net.goutalk.glcs.module.system.service.IOauthService;
import com.xkcoding.http.config.HttpConfig;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.scope.*;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.request.*;
import me.zhyd.oauth.utils.AuthScopeUtils;
import net.goutalk.glcs.module.organization.service.*;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author: tanyujie
 * @Date: 2023/8/8 10:40
 */
@Service
@AllArgsConstructor
public class OauthServiceImpl implements IOauthService {

    private final IUserService userService;

    private final IUserDeptRelationService userDeptRelationService;

    private final IUserPostRelationService userPostRelationService;

    private final IPostService postService;

    private final IDepartmentService departmentService;

    private final RedisUtil redisUtil;


    private final LicenseConfig licenseConfig;
    
    private final DingtalkConfig dingtalkConfig;

    private final WechatEnterpriseConfig wechatEnterpriseConfig;

    @Override
    @SneakyThrows
    public void oAuthLogin(String source, AuthCallback callback, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {

        String prevPage = httpServletRequest.getHeader("Referer");

        if (licenseConfig.getEnabled()) {
            //查出所有在线用户
            List<String> onlineUser = StpUtil.searchSessionId("", 0, Integer.MAX_VALUE);

            //如果已经登录人数超过授权人数  不允许登录
            if (onlineUser.size() >= licenseConfig.getLoginMax()) {
                httpServletResponse.sendRedirect("http://localhost:3100/#/login?error=登录人数超过授权人数，无法登录，请联系管理员！");
            }
        }

        AuthRequest authRequest = getAuthRequest(source);
        AuthResponse response = authRequest.login(callback);


        User loginUser = new User();

        if(response.getCode() == 2000){

            if(source.equals("dingtalk")){

                DingTalkModel dingTalkModel = Convert.convert(DingTalkModel.class,response.getData());
                DingTalkUserInfoModel dingTalkUserInfoModel = dingTalkModel.getRawUserInfo();

                DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
                OapiGettokenRequest request = new OapiGettokenRequest();
                request.setAppkey(dingtalkConfig.getAppKey());
                request.setAppsecret(dingtalkConfig.getAppSecret());
                request.setHttpMethod(HttpMethod.GET.name());
                OapiGettokenResponse res = client.execute(request);

                DingTalkClient uniClient = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid");
                OapiUserGetbyunionidRequest uniRequset = new OapiUserGetbyunionidRequest();
                uniRequset.setUnionid(dingTalkUserInfoModel.getUnionid());
                OapiUserGetbyunionidResponse uniResp = uniClient.execute(uniRequset, res.getAccessToken());


                DingTalkClient userInfoClient = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
                OapiV2UserGetRequest userInfoReq = new OapiV2UserGetRequest();
                userInfoReq.setUserid(uniResp.getResult().getUserid());
                userInfoReq.setLanguage("zh_CN");
                OapiV2UserGetResponse userInfoResp = userInfoClient.execute(userInfoReq, res.getAccessToken());


                LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(User::getMobile,userInfoResp.getResult().getMobile());
                loginUser = userService.getOne(queryWrapper);

                //如果扫码登录 没有此账号 默认新增
                if (loginUser == null) {
                    httpServletResponse.sendRedirect("http://localhost:3100/#/login?error=没有找到所关联的账户");
                    return;
                }
            }
            else{
                WechatEnterpriseModel wechatEnterpriseModel = Convert.convert(WechatEnterpriseModel.class,response.getData());
                WechatEnterpriseUserInfoModel rawUserInfo = wechatEnterpriseModel.getRawUserInfo();

                LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(User::getMobile,rawUserInfo.getMobile());
                loginUser = userService.getOne(queryWrapper);
                //如果扫码登录 没有此账号 默认新增
                if (loginUser == null) {

                    httpServletResponse.sendRedirect("http://localhost:3100/#/login?error=没有找到所关联的账户");
                    return;

//                    loginUser = new User();
//                    initUser(source,response.getData(),loginUser);
//
//                    loginUser = new User();
//                    loginUser.setName(rawUserInfo.getName());
//                    loginUser.setUserName(rawUserInfo.getMobile());
//                    loginUser.setEmail(rawUserInfo.getEmail());
//                    loginUser.setAvatar(rawUserInfo.getAvatar());
//                    loginUser.setMobile(rawUserInfo.getMobile());
//                    loginUser.setNickName(wechatEnterpriseModel.getNickname());
//                    loginUser.setGender(rawUserInfo.getGender());
//                    loginUser.setEnabledMark(rawUserInfo.getEnable());
//                    userService.save(loginUser);
                }
            }



        }
        else {
            httpServletResponse.sendRedirect("http://localhost:3100/#/login?error=" + response.getMsg());
        }

        if (loginUser.getEnabledMark() == EnabledMark.DISABLED.getCode()) {

            httpServletResponse.sendRedirect("http://localhost:3100/#/login?error=账户未启用");
            return;
        }

        //此登录接口登录web端
        StpUtil.login(loginUser.getId(), "PC");

        SaSession tokenSession = StpUtil.getTokenSession();

        List<UserDeptRelation> userDeptRelations = userDeptRelationService.list(Wrappers.lambdaQuery(UserDeptRelation.class)
                .eq(UserDeptRelation::getUserId, StpUtil.getLoginIdAsLong()));

        List<UserPostRelation> userPostRelations = userPostRelationService.list(Wrappers.lambdaQuery(UserPostRelation.class)
                .eq(UserPostRelation::getUserId, StpUtil.getLoginIdAsLong()));

        //获取登陆人所选择的身份缓存
        String postId = redisUtil.get(GlobalConstant.LOGIN_IDENTITY_CACHE_PREFIX + loginUser.getId());

        Post post = new Post();
        if (StrUtil.isNotBlank(postId)) {
            post = postService.getById(postId);
        }

        if (userPostRelations.size() > 0) {
            List<Long> postIds = userPostRelations.stream().map(UserPostRelation::getPostId).collect(Collectors.toList());

            List<Post> postList = postService.listByIds(postIds);
            if (StrUtil.isBlank(postId) && CollectionUtils.isNotEmpty(postList)) {
                post = postList.get(0);
            }
            tokenSession.set(GlobalConstant.LOGIN_USER_POST_INFO_KEY, post);
            tokenSession.set(GlobalConstant.LOGIN_USER_POST_LIST_KEY, postList);
            loginUser.setPostId(post.getId());

            //将登陆人所选择的身份缓存起来
            //切换身份的时候 会一起修改
            redisUtil.set(GlobalConstant.LOGIN_IDENTITY_CACHE_PREFIX + loginUser.getId(), post.getId());
        }

        if (userDeptRelations.size() > 0) {
            // 存当前用户所有部门到缓存
            List<Long> departmentIds = userDeptRelations.stream().map(UserDeptRelation::getDeptId).collect(Collectors.toList());
            List<Department> departmentList = departmentService.listByIds(departmentIds);
            tokenSession.set(GlobalConstant.LOGIN_USER_DEPT_LIST_KEY, departmentList);
            //如果此人有岗位  使用岗位的deptId  找到当前组织机构
            if (ObjectUtil.isNotNull(post.getId())) {
                Department department = departmentService.getById(post.getDeptId());
                tokenSession.set(GlobalConstant.LOGIN_USER_DEPT_INFO_KEY, department);
                loginUser.setDepartmentId(department.getId());
            } else {

                Department department = departmentList.get(0);

                tokenSession.set(GlobalConstant.LOGIN_USER_DEPT_INFO_KEY, department);

                loginUser.setDepartmentId(department.getId());
            }

        }

        //根据登录信息  将post  和 department 信息存入用户信息中
        tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, loginUser);

        httpServletResponse.sendRedirect("http://localhost:3100/#/login?token=" + StpUtil.getTokenValue());

    }

    /**
     * 根据具体的授权来源，获取授权请求工具类
     *
     * @param source
     * @return
     */
    public AuthRequest getAuthRequest(String source) {
        AuthRequest authRequest = null;
        switch (source.toLowerCase()) {
            case "dingtalk":
                authRequest = new AuthDingTalkRequest(AuthConfig.builder()
                        .clientId(dingtalkConfig.getAppKey())
                        .clientSecret(dingtalkConfig.getAppSecret())
                        .redirectUri(dingtalkConfig.getRedirectUri())
                        .agentId(dingtalkConfig.getAgentid())
                        .build());
                break;
            case "baidu":
                authRequest = new AuthBaiduRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/baidu")
                        .scopes(Arrays.asList(
                                AuthBaiduScope.BASIC.getScope(),
                                AuthBaiduScope.SUPER_MSG.getScope(),
                                AuthBaiduScope.NETDISK.getScope()
                        ))
//                        .clientId("")
//                        .clientSecret("")
//                        .redirectUri("http://localhost:9001/oauth/baidu/callback")
                        .build());
                break;

            case "weibo":
                authRequest = new AuthWeiboRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://dblog-web.zhyd.me/oauth/callback/weibo")
                        .scopes(Arrays.asList(
                                AuthWeiboScope.EMAIL.getScope(),
                                AuthWeiboScope.FRIENDSHIPS_GROUPS_READ.getScope(),
                                AuthWeiboScope.STATUSES_TO_ME_READ.getScope()
                        ))
                        .build());
                break;
            case "coding":
                authRequest = new AuthCodingRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://dblog-web.zhyd.me/oauth/callback/coding")
                        .domainPrefix("")
                        .scopes(Arrays.asList(
                                AuthCodingScope.USER.getScope(),
                                AuthCodingScope.USER_EMAIL.getScope(),
                                AuthCodingScope.USER_PHONE.getScope()
                        ))
                        .build());
                break;
            case "oschina":
                authRequest = new AuthOschinaRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/oschina")
                        .build());
                break;
            case "alipay":
                // 支付宝在创建回调地址时，不允许使用localhost或者127.0.0.1，所以这儿的回调地址使用的局域网内的ip
                authRequest = new AuthAlipayRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .alipayPublicKey("")
                        .redirectUri("https://www.zhyd.me/oauth/callback/alipay")
                        .build());
                break;
            case "qq":
                authRequest = new AuthQqRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/qq")
                        .build());
                break;
            case "wechat_open":
                authRequest = new AuthWeChatOpenRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://www.zhyd.me/oauth/callback/wechat")
                        .build());
                break;
            case "csdn":
                authRequest = new AuthCsdnRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://dblog-web.zhyd.me/oauth/callback/csdn")
                        .build());
                break;
            case "taobao":
                authRequest = new AuthTaobaoRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://dblog-web.zhyd.me/oauth/callback/taobao")
                        .build());
                break;
            case "google":
                authRequest = new AuthGoogleRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/google")
                        .scopes(AuthScopeUtils.getScopes(AuthGoogleScope.USER_EMAIL, AuthGoogleScope.USER_PROFILE, AuthGoogleScope.USER_OPENID))
                        // 针对国外平台配置代理
                        .httpConfig(HttpConfig.builder()
                                .timeout(15000)
                                .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080)))
                                .build())
                        .build());
                break;
            case "facebook":
                authRequest = new AuthFacebookRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("https://justauth.cn/oauth/callback/facebook")
                        .scopes(AuthScopeUtils.getScopes(AuthFacebookScope.values()))
                        // 针对国外平台配置代理
                        .httpConfig(HttpConfig.builder()
                                .timeout(15000)
                                .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080)))
                                .build())
                        .build());
                break;
            case "douyin":
                authRequest = new AuthDouyinRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://dblog-web.zhyd.me/oauth/callback/douyin")
                        .build());
                break;
            case "linkedin":
                authRequest = new AuthLinkedinRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/linkedin")
                        .scopes(null)
                        .build());
                break;
            case "microsoft":
                authRequest = new AuthMicrosoftRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/microsoft")
                        .scopes(Arrays.asList(
                                AuthMicrosoftScope.USER_READ.getScope(),
                                AuthMicrosoftScope.USER_READWRITE.getScope(),
                                AuthMicrosoftScope.USER_READBASIC_ALL.getScope(),
                                AuthMicrosoftScope.USER_READ_ALL.getScope(),
                                AuthMicrosoftScope.USER_READWRITE_ALL.getScope(),
                                AuthMicrosoftScope.USER_INVITE_ALL.getScope(),
                                AuthMicrosoftScope.USER_EXPORT_ALL.getScope(),
                                AuthMicrosoftScope.USER_MANAGEIDENTITIES_ALL.getScope(),
                                AuthMicrosoftScope.FILES_READ.getScope()
                        ))
                        .build());
                break;
            case "mi":
                authRequest = new AuthMiRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://dblog-web.zhyd.me/oauth/callback/mi")
                        .build());
                break;
            case "toutiao":
                authRequest = new AuthToutiaoRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://dblog-web.zhyd.me/oauth/callback/toutiao")
                        .build());
                break;
            case "teambition":
                authRequest = new AuthTeambitionRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://127.0.0.1:8443/oauth/callback/teambition")
                        .build());
                break;
            case "pinterest":
                authRequest = new AuthPinterestRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("https://eadmin.innodev.com.cn/oauth/callback/pinterest")
                        // 针对国外平台配置代理
                        .httpConfig(HttpConfig.builder()
                                .timeout(15000)
                                .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080)))
                                .build())
                        .build());
                break;
            case "renren":
                authRequest = new AuthRenrenRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://127.0.0.1:8443/oauth/callback/teambition")
                        .build());
                break;
            case "stack_overflow":
                authRequest = new AuthStackOverflowRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("((")
                        .redirectUri("http://localhost:8443/oauth/callback/stack_overflow")
                        .stackOverflowKey("")
                        .build());
                break;
            case "huawei":
                authRequest = new AuthHuaweiRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://127.0.0.1:8443/oauth/callback/huawei")
                        .scopes(Arrays.asList(
                                AuthHuaweiScope.BASE_PROFILE.getScope(),
                                AuthHuaweiScope.MOBILE_NUMBER.getScope(),
                                AuthHuaweiScope.ACCOUNTLIST.getScope(),
                                AuthHuaweiScope.SCOPE_DRIVE_FILE.getScope(),
                                AuthHuaweiScope.SCOPE_DRIVE_APPDATA.getScope()
                        ))
                        .build());
                break;
            case "wechat_enterprise":
                authRequest = new AuthWeChatEnterpriseQrcodeRequest(AuthConfig.builder()
                        .clientId(wechatEnterpriseConfig.getAppKey())
                        .clientSecret(wechatEnterpriseConfig.getAppSecret())
                        .redirectUri(wechatEnterpriseConfig.getRedirectUri())
                        .agentId(wechatEnterpriseConfig.getAgentid())
                        .build());
                break;
            case "kujiale":
                authRequest = new AuthKujialeRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://dblog-web.zhyd.me/oauth/callback/kujiale")
                        .build());
                break;
            case "gitlab":
                authRequest = new AuthGitlabRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/gitlab")
                        .scopes(AuthScopeUtils.getScopes(AuthGitlabScope.values()))
                        .build());
                break;
            case "meituan":
                authRequest = new AuthMeituanRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/meituan")
                        .build());
                break;
            case "eleme":
                authRequest = new AuthElemeRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://dblog-web.zhyd.me/oauth/callback/eleme")
                        .build());
                break;

            case "twitter":
                authRequest = new AuthTwitterRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("https://threelogin.31huiyi.com/oauth/callback/twitter")
                        // 针对国外平台配置代理
                        .httpConfig(HttpConfig.builder()
                                .timeout(15000)
                                .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080)))
                                .build())
                        .build());
                break;
            case "wechat_mp":
                authRequest = new AuthWeChatMpRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("")
                        .build());
                break;
            case "aliyun":
                authRequest = new AuthAliyunRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/aliyun")
                        .build());
                break;
            case "xmly":
                authRequest = new AuthXmlyRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/xmly")
                        .build());
                break;
            case "feishu":
                authRequest = new AuthFeishuRequest(AuthConfig.builder()
                        .clientId("")
                        .clientSecret("")
                        .redirectUri("http://localhost:8443/oauth/callback/feishu")
                        .build());
                break;
            default:
                break;
        }
        if (null == authRequest) {
            throw new AuthException("未获取到有效的Auth配置");
        }
        return authRequest;
    }



}
